/**************************************************************************************
 *  Copyright (c)  2010  Leszek Karcz.
 *  Permission is granted to copy, distribute and/or modify this document
 *  under the terms of the GNU Free Documentation License, Version 1.3
 *  or any later version published by the Free Software Foundation;
 *  with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
 *  A copy of the license is included in the section entitled "GNU
 *  Free Documentation License". 
 **************************************************************************************/
package data;

/**************************************************************************************
 * Author(s): Leszek Karcz
 * This class is a structure for movement in hexagonal environment
 **************************************************************************************/
public abstract class HexMap {
	private int width, height;
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * HexMap constructor
	 **************************************************************************************/
	public HexMap()
	{
		
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * sets the map width
	 **************************************************************************************/
	protected void setWidth(int width)
	{
		this.width=width;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * sets the map height
	 **************************************************************************************/
	public void setHeight(int height)
	{
		this.height=height;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns the map width
	 **************************************************************************************/
	public int getWidth()
	{
		return this.width;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Returns the map height
	 **************************************************************************************/
	public int getHeight()
	{
		return this.height;
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * Check if the position is within map bounds
	 **************************************************************************************/
	public boolean verifyPosition(int x, int y)
	{
		if((x>=0) && (y>=0) && (x<this.width) && (y<this.height))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns new position based on current position and direction
	 **************************************************************************************/
	public int[] getAdjacentPosition(int x, int y, int direction)
	{
		int newX,newY;
		int mod=x%2;
		
		if(direction==0) //up
		{
			newX=x;
			newY=y-1;
		}
		else if(direction==1) //up-right
		{
			if((mod) == 0)
			{
				newX=x+1;
				newY=y-1;
			}
			else
			{
				newX=x+1;
				newY=y;
			}
		}
		else if(direction==2) //down-right
		{
			if((mod) == 0)
			{
				newX=x+1;
				newY=y;
			}
			else
			{
				newX=x+1;
				newY=y+1;
			}
		}
		else if(direction==3) //down
		{
				newX=x;
				newY=y+1;
		}
		else if(direction==4) //down-left
		{
			if((mod) == 0)
			{
				newX=x-1;
				newY=y;
			}
			else
			{
				newX=x-1;
				newY=y+1;
			}
		}
		else if(direction==5) //top-left
		{
			if((mod) == 0)
			{
				newX=x-1;
				newY=y-1;
			}
			else
			{
				newX=x-1;
				newY=y;
			}
		}
		else
		{
			newX=x;
			newY=y;
		}
			return new int[]{newX,newY};
	}
	
	/**************************************************************************************
	 * Author(s): Leszek Karcz
	 * returns list of coordinates of tiles within area with center at [x,y] coordinates
	 * and certain radius
	 **************************************************************************************/
	public LinkedList<int[]> getArea(int x, int y, int radius)
	{
		LinkedList<int[]> area = new LinkedList<int[]>();
		int[] position=new int[]{x,y};
		int mod=x%2;		
		
		//for each level of radius add a "ring" of positions
		for(int r=0;r<=radius;r++)
		{
			//center position
			if(r==0)
			{
				//check if position is valid
				if(verifyPosition(x,y))
				{
					//add position to the list
					area.addBack(new int[]{x,y,0});
				}
			}
			else
			{
				//start with topmost position (topmost position is actually added last)
				position[0]=x;
				position[1]=y-r;
				for(int i=0;i<r;i++) //top-right edge
				{
					//get new position
					position=this.getAdjacentPosition(position[0], position[1], 2);
					//check if position is valid
					if(verifyPosition(position[0], position[1]))
					{
						//add position to the list
						area.addBack(new int[]{position[0], position[1],r});
					}
				}
				for(int i=0;i<r;i++) //right edge
				{
					//get new position
					position=this.getAdjacentPosition(position[0], position[1], 3);
					//check if position is valid
					if(verifyPosition(position[0], position[1]))
					{
						//add position to the list
						area.addBack(new int[]{position[0], position[1],r});
					}
				}
				for(int i=0;i<r;i++) //bottom-right edge
				{
					//get new position
					position=this.getAdjacentPosition(position[0], position[1], 4);
					//check if position is valid
					if(verifyPosition(position[0], position[1]))
					{
						//add position to the list
						area.addBack(new int[]{position[0], position[1],r});
					}
				}
				for(int i=0;i<r;i++) //bottom-left edge
				{
					//get new position
					position=this.getAdjacentPosition(position[0], position[1], 5);
					//check if position is valid
					if(verifyPosition(position[0], position[1]))
					{
						//add position to the list
						area.addBack(new int[]{position[0], position[1],r});
					}
				}
				for(int i=0;i<r;i++) //left edge
				{
					//get new position
					position=this.getAdjacentPosition(position[0], position[1], 0);
					//check if position is valid
					if(verifyPosition(position[0], position[1]))
					{
						//add position to the list
						area.addBack(new int[]{position[0], position[1],r});
					}
				}
				for(int i=0;i<r;i++) //top-left edge
				{
					//get new position
					position=this.getAdjacentPosition(position[0], position[1], 1);
					//check if position is valid
					if(verifyPosition(position[0], position[1]))
					{
						//add position to the list
						area.addBack(new int[]{position[0], position[1],r});
					}
				}				
			}
		}
		return area;
	}	
	
	public boolean checkRadius(int sourceX, int sourceY, int desX, int desY, int radius){
		LinkedList<int []> tiles = this.getArea(sourceX, sourceX, radius);
		int [] temp;
		
		for(int i = 0; !tiles.isEmpty(); i++){
			temp = tiles.removeFirst();
			
			if(temp[0] == desX && temp[1] == desY)
				return true;
		}
		return true;
	}
	
}
